﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace System.Windows.JControls.JColorPicker
{
    /// <summary>
    /// 
    /// </summary>
    partial class ColorWheel : Control
    {
        public event EventHandler PaintAreaChanged;

        /// <summary>
        /// Fired, when the user clicked in a area from the color wheel.
        /// </summary>
        public event EventHandler SelectedColorChanged;

        private List<PointF> piePath = new List<PointF>();  // points from the wheel
        private List<Color> colorWheel = new List<Color>(); // colors
        private PointF selectorPoint;                       // the last color, which the user has selected
        private Point lastMousePoint;                       // the last location from the mouse point
        private HSB hsbColor;                               // the selected color as a hsb-value
        private bool _mouseIsDown;                          // when  the user pressed on the mouse-button

        /// <summary>
        /// Initialze the class and generate the pie and colors from the color wheel.
        /// </summary>
        public ColorWheel()
        {
            base.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
            InitializeComponent();

            // Fill lists
            FillPathes();

            // MousePoint
            PointF pointcenter = Center();
            lastMousePoint = new Point(Convert.ToInt32(pointcenter.X), Convert.ToInt32(pointcenter.Y));
            hsbColor = new HSB();
            //
            _mouseIsDown = false;
        }

        /// <summary>
        /// Generate the pie
        /// </summary>
        private void FillPathes()
        {

            colorWheel.Clear();
            piePath.Clear();
            

            PointF center = Center();
            float radius = Radius();
            float angle = 0;
            float fullcircle = 360;
            float step = 2;
            while (angle < fullcircle)
            {
                piePath.Add(PiePathPoint(center,radius,angle));
                
                HSB hsb = new HSB(angle, 1, 1);
                RGB rgb = ColorSpaceHelper.HSBtoRGB(hsb);
                colorWheel.Add(Color.FromArgb(255, rgb.Red, rgb.Green, rgb.Blue));
                angle += step;
            }
        }

        /// <summary>
        /// A point of the border from the pie
        /// </summary>
        /// <param name="center">The center from the wheel</param>
        /// <param name="radius">The radius from the wheel</param>
        /// <param name="angle">The angle</param>
        /// <returns></returns>
        private PointF PiePathPoint(PointF center, float radius, float angle)
        {
            float angleR = angle * (float)(Math.PI / 180);
            float x = center.X + ((float)Math.Cos(angleR) * radius);
            float y = center.Y - ((float)Math.Sin(angleR) * radius);

            return new PointF((float)x, (float)y);
        }

        internal void SetColorFromMousePoint(Point mousePoint)
        {
            if (lastMousePoint != Point.Empty)
            {
                Invalidate(new Rectangle(lastMousePoint.X - 10, lastMousePoint.Y - 10, 20, 20));
            }

            lastMousePoint = mousePoint;

            SetColor();

            if (hsbColor.Saturation == 1)
            {
                Invalidate();
            }
            else
            {
                Invalidate(new Rectangle(lastMousePoint.X - 10, lastMousePoint.Y - 10, 20, 20));
            }

            ColorSelectorRectangle();
        }

        private void SetColor()
        {
            PointF center = Center();
            float radius = Radius();
            float dx = Math.Abs(lastMousePoint.X - center.X);
            float dy = Math.Abs(lastMousePoint.Y - center.Y);
            float angle = (float)Math.Atan(dy / dx) / (float)Math.PI * 180;

            if (Single.IsNaN(angle))
            {
                angle = 0.0f;
            }
            
            double dist = Math.Pow((Math.Pow(dx, 2) + (Math.Pow(dy, 2))), 0.5);

            if (dist > radius)
            {
                dist = radius;
            }

            float saturation = (float)dist / radius;

            if (lastMousePoint.X < center.X)
            {
                angle = 180 - angle;
            }

            if (lastMousePoint.Y > center.Y)
            {
                angle = 360 - angle;
            }

            hsbColor = new HSB(angle, saturation, 1);
            OnSelectedColorChanged(this, null);
        }

        private void ColorSelectorRectangle()
        {
            double angleR = hsbColor.Hue * Math.PI / 180;
            PointF center = Center();
            double radius = Radius();
            radius *= hsbColor.Saturation;
            double x = center.X + Math.Cos(angleR) * radius;
            double y = center.Y - Math.Sin(angleR) * radius;
            selectorPoint = new PointF((float)x, (float)y);
        }

        /// <summary>
        /// Get the center from the paint area.
        /// </summary>
        /// <returns></returns>
        internal PointF Center()
        {
            Rectangle rectpaint = PaintArea();
            return new PointF(rectpaint.Left + (rectpaint.Width / 2), rectpaint.Top + (rectpaint.Height / 2));
        }

        /// <summary>
        /// Get the radius.
        /// </summary>
        /// <returns></returns>
        internal float Radius()
        {
            Rectangle rectpaint = PaintArea();
            return Math.Min((rectpaint.Size.Width / 2), (rectpaint.Size.Height / 2));
        }

        /// <summary>
        /// Set the area, where the pie would be painted.
        /// </summary>
        private Rectangle PaintArea()
        {
            if (Padding.All > -1)
            {
                return new Rectangle(ClientRectangle.Left + Padding.All,
                                                ClientRectangle.Top + Padding.All,
                                                ClientRectangle.Width - Padding.Horizontal,
                                                ClientRectangle.Height - Padding.Vertical);
            }
            else
            {
                return new Rectangle(ClientRectangle.Left + Padding.Left,
                                                ClientRectangle.Top + Padding.Top,
                                                ClientRectangle.Width - Padding.Horizontal,
                                                ClientRectangle.Height - Padding.Vertical);
            }
        }

        /// <summary>
        /// Get the selected value from HSV-Color
        /// </summary>
        internal HSB SelectedHSBColor
        {
            get { return hsbColor; }
            set
            {
                hsbColor = value;

                ColorSelectorRectangle();
                SetColorFromMousePoint(Point.Ceiling(selectorPoint));
            }
        }

        internal bool MouseIsDown
        {
            get { return _mouseIsDown; }
            set { _mouseIsDown = value; }
        }

        #region Override methods

        /// <summary>
        /// <b>Override:</b> When occurs the paint event.
        /// </summary>
        /// <param name="pe"></param>
        protected override void OnPaint(PaintEventArgs pe)
        {
            pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;

            if (piePath == null || piePath.Count == 0)
            {
                FillPathes();
            }

            Rectangle rectpaint = PaintArea();
            
            // Paint color wheel
            using (PathGradientBrush m_brush = new PathGradientBrush(piePath.ToArray(), WrapMode.Clamp))
            {
                m_brush.CenterPoint = Center();
                m_brush.CenterColor = Color.White;
                m_brush.SurroundColors = colorWheel.ToArray();

                // Kreis zeichnen
                pe.Graphics.FillPie(m_brush,
                                    rectpaint.X,
                                    rectpaint.Y,
                                    rectpaint.Width,
                                    rectpaint.Height, 
                                    0, 360);
            }

            // Draw cursor
            if (!Single.IsNaN(selectorPoint.X) && !Single.IsNaN(selectorPoint.Y))
            {
                pe.Graphics.DrawImage(Resources.ResourcesColorWheel.CursorWheel,
                                              selectorPoint.X - (Resources.ResourcesColorWheel.CursorWheel.Width / 2),
                                              selectorPoint.Y - (Resources.ResourcesColorWheel.CursorWheel.Height / 2));
            }
        }

        

        /// <summary>
        /// <b>Override:</b> When the value from the property Size is changed.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnSizeChanged(EventArgs e)
        {
            FillPathes();
            base.OnSizeChanged(e);
        }

        /// <summary>
        /// <b>Override:</b> When the property Padding would be changed.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaddingChanged(EventArgs e)
        {
            FillPathes();
            Invalidate();
            base.OnPaddingChanged(e);
        }

        #endregion

        #region Occurs Events

        /// <summary>
        /// Occurs, when the area for painting is changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        protected virtual void OnPaintAreaChanged(object sender, EventArgs args)
        {
            if (PaintAreaChanged != null)
            {
                PaintAreaChanged(sender, args);
            }
        }

        /// <summary>
        /// Occurs, when the selected color is changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        protected virtual void OnSelectedColorChanged(object sender, EventArgs args)
        {
            if (SelectedColorChanged != null)
            {
                SelectedColorChanged(sender, args);
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            _mouseIsDown = true;
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (_mouseIsDown && e.Button == MouseButtons.Left)
            {
                SetColorFromMousePoint(e.Location);
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);

            _mouseIsDown = false;
        }

        #endregion
    }
}
